UTF-8是Unicode的一种实现方式。javascript把每个汉字当成一个字符,而在后台PHP是按照UTF-8计算,每个汉字由三个字节组成,故中文输入到了后台长度会变成字数的三倍。所以在输入框里面有中文的时候maxlength限制就不再可靠。
精确获取字符长度
由于maxlength不再可靠,需要有方法获取任意输入的字节数。针对中文输入使用将其转码并使用特殊方法计算其长度便可达成精确控制输入长度的目的。encodeURIComponent方法比较好用:1
The encodeURIComponent() function encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two "surrogate" characters).
该方法能将UTF-8编码的输入文字转码成对应的多字节字符串。 例如:
1 | encodeURIComponent('我的问题') "%E6%88%91%E7%9A%84%E9%97%AE%E9%A2%98" |
每个中文字转成三个字节冠以%开头。匹配出这样的pattern并计算长度,使用javascript的正则表达式:
1 | /%[A-F\d]{2}/g |
并临时替换成一个英文字符,那么长度就可以准确得到:1
2
3encodeURIComponent('abc我的问题abc').replace(/%[A-F\d]{2}/g, 'i') //"abciiiiiiiiiiiiabc"
encodeURIComponent('abc我的问题abc').replace(/%[A-F\d]{2}/g, 'i').length
//18
textarea 中的换行符
与input相比,textarea还能输入回车换行,但是换行在javascript计算长度的时候却少计算一个字符,这样也会造成跟PHP后台不一致的情况,其实java后台也是一样的。 例如只有一个换行符的encode前端结果:
1 | encodeURIComponent($('textarea').val()) "%0A" |
而到了后台该换行符变成:0D 0A。 产生这个问题的原因是换行符在HTTP传输的过程中发生转换,由‘\n’变成‘\r\n’。 因此在计算textarea值时在遇到换行符时记得长度额外加1。
代码实现
针对input
1 | $("input").on('keydown keyup paste',function(){ |
针对textarea:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23$("textarea").on('keydown keyup paste',function(){
var $that = $(this),
maxlength = $that.attr('maxlength');
if($.isNumeric(maxlength) && maxlength > 0){
var realLen = encodeURIComponent($that.val()).replace(/%[A-F\d]{2}/g, 'U').length;
var newLines = $that.val().match(/(\r\n|\n|\r)/g);
if (newLines !== null) {
realLen += newLines.length;
}
var currLen = $that.val().length;
if (realLen > maxlength) {
do {
$that.val($that.val().substr(0, currLen));
currLen --;
realLen = encodeURIComponent($that.val()).replace(/%[A-F\d]{2}/g, 'U').length;
newLines = $that.val().match(/(\r\n|\n|\r)/g);
if (newLines !== null) {
realLen += newLines.length;
}
} while (realLen > maxlength);
}
};
});